/*
 * Copyright (c) 2006 FORTHnet, S.A. All Rights Reserved.
 *
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 *
 * $Id$
 */

package net.googlecode.weblogic_jaxb1codec;

import java.util.ArrayList;
import java.util.ListIterator;

import weblogic.xml.stream.ElementFactory;
import weblogic.xml.stream.XMLOutputStream;
import weblogic.xml.stream.XMLStreamException;

/**
 *
 *
 * @author <a href="mailto:kana@forthnet.gr"/>
 * @version $Id$
 */
public class NamespaceStack {

    private static class PrefixMapping {
        public String prefix;
        public String uri;
        
        public PrefixMapping(String prefix, String uri) {
            this.prefix = prefix;
            this.uri = uri;
        }

        public boolean equals(Object obj) {
            if (obj == this) return true;
            if ( !(obj instanceof PrefixMapping)) return false;
            
            PrefixMapping p = (PrefixMapping) obj;
            return (prefix + uri).equals(p.prefix + p.uri);
        }

        public int hashCode() {
            return System.identityHashCode(prefix + uri);
        }

        public String toString() {
            return "{"+uri+"}"+prefix;
        }
    }

    /**
     * Contains all prefixMappings, with last those that have not been written yet.
     */
    private ArrayList prefixMappings = new ArrayList();
    
    /**
     * An index into the {@link #prefixMappings} pointing to the first non-written-yet prefix. 
     */
    private int prefixMappingsIndex;
    
//    private PrefixMapping cachedMapping;
    
    private boolean debug;
    
    public NamespaceStack(boolean debug) {
        this.debug = debug;
    }
    
    public void addNewNamespaceDeclAttrs(XMLOutputStream outputStream) throws XMLStreamException {
        ListIterator it = prefixMappings.listIterator(prefixMappingsIndex);
        while(it.hasNext()) {
            PrefixMapping pm = (PrefixMapping)it.next();
            outputStream.add(ElementFactory.createNamespaceAttribute(
                    pm.prefix, pm.uri));
            if (debug)
                System.out.print(" xmlns:" + pm.prefix+"=\""+pm.uri+"\"");
        }
        
        prefixMappingsIndex = prefixMappings.size();
    }
    
    public void addNewNamespaceDeclAttrs(StringBuffer sb)  {
        ListIterator it = prefixMappings.listIterator(prefixMappingsIndex);
        while(it.hasNext()) {
            PrefixMapping pm = (PrefixMapping)it.next();
            sb.append(" xmlns" + ( (pm.prefix == null || pm.prefix.length() == 0)? "": ":" + pm.prefix)  
                    + "=\"" 
                    + pm.uri + "\"");
//            System.out.print(" xmlns:" + pm.prefix+"=\""+pm.uri+"\"");
        }
        
        prefixMappingsIndex = prefixMappings.size();
    }
    
    public boolean areNamespaceDeclAttrsPending() {
        return prefixMappingsIndex != prefixMappings.size();
    }
    
    /**
     * Searches top-down the {@link #prefixMappings} for the spec'ed uri, and 
     * (NOT ANYMORE) nupdates the cache for next searches.
     * @param uri
     * @return (null) the preffix found or null.
     */
    public String findPrefix(String uri) {
//        if (cachedMapping != null && cachedMapping.uri.equals(uri))
//            return cachedMapping.prefix;
        
        for (ListIterator it = prefixMappings.listIterator(prefixMappings.size()); it.hasPrevious();) {
            PrefixMapping p = (PrefixMapping)it.previous();
            if (p.uri.equals(uri)) {
//                cachedMapping = p;
                return p.prefix;
            }
        }
        
        return null;
//        throw new AssertionError("Non-mapped-to-prefix uri: "+uri);
    }

    public String findUri(String prefix) {
        for (ListIterator it = prefixMappings.listIterator(prefixMappings.size()); it.hasPrevious();) {
            PrefixMapping p = (PrefixMapping)it.previous();
            if (p.prefix.equals(prefix)) {
                return p.uri;
            }
        }
        
        return null;
    }

    /**
     * Before adding it to the top, it checks that the mapping does not already exists, 
     * and if it is, does nothing and return false.
     * 
     * @param prefix
     * @param uri
     * @param return false if mapping already exists.
     */
    public boolean pushMapping(String prefix, String uri) {
        for (ListIterator it = prefixMappings.listIterator(prefixMappings.size()); it.hasPrevious();) {
            PrefixMapping p = (PrefixMapping)it.previous();
            if (p.uri.equals(uri) && p.prefix.equals(prefix))
                return false;
        }
        this.prefixMappings.add(new PrefixMapping(prefix, uri));
        return true;
    }
    
    public void popMapping(String prefix) {
        PrefixMapping p = (PrefixMapping) prefixMappings.remove(prefixMappings.size() - 1);
        prefixMappingsIndex--;
        if (p.prefix != prefix && prefixMappingsIndex != prefixMappings.size())
            throw new AssertionError("Inconsistent prefixes '"+prefix+"'!='"+p.prefix+"', or "
                    +prefixMappingsIndex+"!="+prefixMappings.size());
    }
}
